<?php

namespace App\Http\Controllers\Api;

use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Ktnw\WechatSupport\Services\WeChatSupportService;
use Ktnw\WechatSupport\Utils\WeChatUtils;
use App\Validators\WeChatLoginValidator;
use Prettus\Validator\Exceptions\ValidatorException;

/**
 * 微信登录相关controller
 * 可根据实际需求扩展
 */
class WeChatLoginController extends Controller
{
    /**
     * 操作成功标志
     */
    private const SUCCESS = 1;

    /**
     * 操作失败标志
     */
    private const FAIL = -1;

    /**
     * 响应代码的key
     */
    private const CODE = "code";

    /**
     * 响应提示信息的key
     */
    private const MESSAGE = "message";

    /**
     * 响应代码的key
     */
    private const DATA = "data";

    private $weChatLoginValidator;

    /**
     * @param WeChatLoginValidator $weChatLoginValidator
     */
    public function __construct(WeChatLoginValidator $weChatLoginValidator)
    {
        $this->weChatLoginValidator = $weChatLoginValidator;
    }

    /**
     * step1: 跳转授权
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ValidatorException
     * @throws Exception
     */
    public function authorize(Request $request): JsonResponse
    {
        $this->weChatLoginValidator->with($request->all())->passesOrFail("authorize");
        // 微信登录成功后，重定向的页面URL.
        $redirectUrl = $request->input('loginRedirect', '');
        // 用户授权后，重定向的服务器端URL.该URL需在微信公众号的网页授权接口中进行设置.
        $authRedirectRoute = config("weChatConfig.auth_redirect_route");
        if (!$authRedirectRoute) {
            throw new Exception("请设置用户授权后重定向的路由");
        }
        $authRedirectUrl = $this->getCurRootPath() . $authRedirectRoute;
        $appId           = config("weChatConfig.app_id");
        $authScope       = config("weChatConfig.auth_scope");
        $userAuthUrl     = WeChatUtils::getAuthorize($appId, $authRedirectUrl, $authScope, $redirectUrl);
        return $this->success($userAuthUrl);
    }

    /**
     * step2: 用户授权后，微信端需重定向回该方法.
     * 业务逻辑:
     * 1.保存用户业务逻辑
     * 2.根据实际需求业务需求进行扩展
     * 3.最后，携带业务参数，重定向到step1中指定的前端页面
     *
     * @param Request $request
     * @return mixed
     * @throws ValidatorException
     * @throws Exception
     */
    public function weChatLogin(Request $request)
    {
        $this->weChatLoginValidator->with($request->all())->passesOrFail("weChatLogin");

        // 用户同意授权后，获取的code
        $code = $request->input("code");

        // 用户自定义参数，微信公众号授权登录后，原值回传。可设置为微信登录成功后跳转的前端页面的URL。
        $state = $request->input("state");

        $appId     = config("weChatConfig.app_id");
        $appSecret = config("weChatConfig.app_secret");
        $tokenInfo = WeChatUtils::findAuthorizationAccessToken($appId, $appSecret, $code);
        if (empty($tokenInfo["access_token"]) || empty($tokenInfo["openid"])) {
            throw new Exception("微信公众号授权登录获取access_token失败");
        }

        // 拉取微信用户信息
        $weChatUser = ["openid" => $tokenInfo["openid"], "unionid" => "", "nickname" => "", "head_img" => ""];
        $authScope  = config("weChatConfig.auth_scope");
        if ($authScope != "snsapi_base") {
            // 拉取微信用户信息
            $wxUserInfo = WeChatUtils::fetchWeChatUserInfo($tokenInfo["access_token"], $tokenInfo["openid"]);
            if ($wxUserInfo) {
                $weChatUser["unionid"]  = Arr::get($wxUserInfo, "unionid", "");
                $weChatUser["nickname"] = Arr::get($wxUserInfo, "nickname", "");
                $weChatUser["head_img"] = Arr::get($wxUserInfo, "headimgurl", "");
            }
        }

        // 保存微信用户
        $weChatUser["wx_key"]      = config("weChatConfig.wx_key");
        $weChatUser["create_time"] = $this->getCurTimeStr();
        $weChatUser["update_time"] = $this->getCurTimeStr();
        $weChatUserId              = WeChatSupportService::saveWeChatUser($weChatUser);
        if ($weChatUserId <= 0) {
            throw new Exception("微信用户信息保存失败");
        }
        $weChatUser = WeChatSupportService::findWeChatUserById($weChatUserId);


        // TODO 绑定微信与本地用户
        // TODO 实现登录后，跳转前端页面，跳转时可自行携带登录凭证等信息;

        return redirect($state);

    }

    /**
     * 微信分享获取签名
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ValidatorException
     */
    public function fetchWxJSConfig(Request $request): JsonResponse
    {
        $this->weChatLoginValidator->with($request->all())->passesOrFail("fetchWxJSConfig");
        $shareUrl  = $request->input('shareUrl');
        $appId     = config("weChatConfig.app_id");
        $appSecret = config("weChatConfig.app_secret");
        $wxKey     = config("weChatConfig.wx_key");

        $timestamp            = time();
        $nonceStr             = Str::random(20);
        $jsapi_ticket         = WeChatUtils::fetchValidWxJsapiTicket($wxKey, $appId, $appSecret);
        $sign['url']          = 'url=' . $shareUrl;
        $sign['noncestr']     = 'noncestr=' . $nonceStr;
        $sign['jsapi_ticket'] = 'jsapi_ticket=' . $jsapi_ticket;
        $sign['timestamp']    = 'timestamp=' . $timestamp;

        // 排序
        sort($sign);
        $signStr   = implode('&', $sign);
        $signature = sha1($signStr);

        $r['appId']     = $appId;
        $r['timestamp'] = $timestamp;
        $r['nonceStr']  = $nonceStr;
        $r['signature'] = $signature;

        return $this->success($r);
    }


    /**
     * 获取当前服务器的域名地址
     */
    private function getCurRootPath(): string
    {
        $appEnv = env("APP_ENV");
        return (strtolower($appEnv) == 'local' ? "http" : "https") . "://" . $_SERVER['HTTP_HOST'];
    }

    /**
     * 获取当前日期的时间字符串
     */
    private function getCurTimeStr()
    {
        return date("Y-m-d H:i:s", time());
    }

    /**
     * 操作成功响应
     * @param mixed $data
     * @param string $message 提示信息
     * @return JsonResponse
     */
    private function success($data = [], string $message = ''): JsonResponse
    {
        return $this->out([self::CODE => self::SUCCESS, self::MESSAGE => $message, self::DATA => $data]);
    }

    /**
     * 操作失败响应
     * @param string $message
     * @return JsonResponse
     */
    private function fail(string $message = ''): JsonResponse
    {
        return $this->out([self::CODE => self::FAIL, self::MESSAGE => $message]);
    }

    /**
     * 响应
     * @param $data
     * @return mixed
     */
    private function out($data): JsonResponse
    {
        return response()->json($data)
            ->header('Content-Type', 'text/json')
            ->setEncodingOptions(JSON_UNESCAPED_UNICODE);
    }

}
